【扩展KMP】【树状数组优化dp】字符串游戏



tsy:衣服串你会啥? me:会乱搞!



这题。。我记得当时写了个。。。搜索。。。各种MLE掉了………… = =

正解呢 是扩展KMP+树状数组优化dp。。


先来说扩展KMP怎么写。。。这道题只用对自己跑扩展KMP 所以就只用写一个函数就行了

inline void e_kmp()
{
	int j = 0, last = 1;
	for(; j < len && s[j] == s[j + 1]; ++j);
	p[0] = len; p[1] = j;
	
	for(int i = 2; i < len; ++i)
	{
		int itmax = p[i - last], already = max(0, last + p[last] - i);
		if(itmax < already) p[i] = itmax;
		else 
		{
			p[i] = already; last = i;
			while(i + p[i] < len && s[i + p[i]] == s[p[i]]) ++p[i];
		}
	}
}

数组p[i]表示 s[0...j] == s[i...k] 的j的最大值 首先初始化 p[0] 和 p[1] 然后从 p[2] 开始推。。

画个比较丑的图以防我以后忘掉了。。相同颜色的区间表示这两个字串完全相同。。。


last表示( last + p[last] )最大的那个位置 也就是说最远已经匹配到哪里了

然后我们现在要匹配的是i。。假如 itmax < already 那么 i-last可以匹配多少(值就是itmax) i就可以匹配多少 (因为already段是相同的)

如果 itmax >= already 至少可以证明 [i...i+already] 是一定匹配的 那么就从这个地方开始继续尝试匹配

同时可以看出 如果 itmax > already 那么i就会匹配得比之前的last还远 那么i就变成last。。


然后就是dp了。。设 dp[i] 表示把一个空串变成 A[1...i] 需要的最小费用 那么 dp[i] 初值即为 i * x

考虑怎么更新 对于位置 i * 2 如果 p[i] >= i 那么就可以直接复制一遍这个串 dp[i * 2] = min(dp[i * 2], dp[i] + y) 

对于位置 [i+1, i+p[i]] 从i转移是既需要剪裁又需要复制的 令这个位置为k dp[k]  = min(dp[k], dp[i] + y + z)


直接转移是n^2的 我们发现可以用线段树优化 每次更新[i + 1, i + p[i]] 

然后维护区间最值也是可以用树状数组的(网上偷了份代码T_T)

void Init(int n){
     for(int i=1;i<=n;i++){
          idx[i]=num[i];
          for(int j=1;j<Lowbit(i);j<<=1){
               idx[i]=MAX(idx[i],idx[i-j]);
          }
     }
}
int Query(int l,int r){
     int ans=num[r];
     while(true){
          ans=MAX(ans,num[r]);
          if(r==l) break;
          for(r-=1;r-l>=Lowbit(r);r-=Lowbit(r)){
               ans=MAX(ans,idx[r]);
          }
     }
     return ans;
}
void Modify(int p,int v,int n){
    num[p]=v;
    for(int i=p;i<=n;i+=Lowbit(i)){
        idx[i]=v;
        for(int j=1;j<Lowbit(i);j<<=1){
            idx[i]=MAX(idx[i],idx[i-j]);
        }
    }
}

但是这题没那么麻烦。。因为对于任何的i 一定是更新 [i+1, x] 由于是从前往后更新 [1, i] 这个区间的值不会再被用到 所以可以直接更新 [1, x] 

然后这题。。。就没了。。。


#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

const int Nmax = 2 * 1e5 + 5; 

char s[Nmax];
int len, p[Nmax], dp[Nmax];
int x, y, z;

namespace BIT{
	int mmin[Nmax];
	inline void init(){ memset(mmin, 0x3f, sizeof(mmin)); }
	inline int lowbit(int x){ return x & (-x); }
	
	void update(int idx, int w)
	{
		for(int i = idx; i; i -= lowbit(i)) 
			mmin[i] = min(mmin[i], w);
	}
	int query(int idx)
	{
		int res = 0x3f3f3f3f;
		for(int i = idx; i <= len; i += lowbit(i))
			res = min(res, mmin[i]);
		return res;
	}
}

inline void e_kmp()
{
	int j = 0, last = 1;
	for(; j < len && s[j] == s[j + 1]; ++j);
	p[0] = len; p[1] = j;
	
	for(int i = 2; i < len; ++i)
	{
		int itmax = p[i - last], already = max(0, last + p[last] - i);
		if(itmax < already) p[i] = itmax;
		else 
		{
			p[i] = already; last = i;
			while(i + p[i] < len && s[i + p[i]] == s[p[i]]) ++p[i];
		}
	}
}

int main()
{
	freopen("catstring.in", "r", stdin);
	freopen("catstring.out", "w", stdout);
	
	ios::sync_with_stdio(false);
	cin >> s >> x >> y >> z;
	len = strlen(s); e_kmp();
	for(int i = 1; i <= len; ++i) dp[i] = i * x;
	
	using namespace BIT; init();
	for(int i = 1; i <= len; ++i)
	{
		dp[i] = min(dp[i], query(i));
		int x = min( len, min((i << 1) - 1, i + p[i]) );
		update(x, dp[i] + y + z);
		if(p[i] >= i) dp[i << 1] = min(dp[i << 1], dp[i] + y);
	}
	
	printf("%d\n", dp[len]);
	
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 首先,KMP算法是一种字符串匹配算法,它的核心是通过预处理模式串,得到一个next数组,用于在匹配过程中快速跳过已经匹配过的部分。 在创建顺序串时,我们需要定义一个结构体来表示顺序串,包含一个字符数组和一个整型变量表示串的长度。在实现KMP算法时,我们需要预处理模式串,计算出next数组。优化next数组的方法是通过递归计算前缀和后缀的最长公共前缀长度,避免重复计算。 具体实现步骤如下: 1. 定义顺序串结构体,包含字符数组和长度变量。 2. 实现KMP算法,预处理模式串,计算出next数组。 3. 优化next数组,通过递归计算前缀和后缀的最长公共前缀长度,避免重复计算。 4. 在匹配过程中,利用next数组快速跳过已经匹配过的部分,提高匹配效率。 总之,KMP算法是一种高效的字符串匹配算法,通过预处理模式串,得到一个next数组,用于在匹配过程中快速跳过已经匹配过的部分。在实现KMP算法时,需要注意优化next数组的方法,避免重复计算,提高匹配效率。 ### 回答2: KMP算法是一种字符串匹配算法,它的核心思想是利用已匹配成功的部分信息,来避免在不必要的地方做重复的比较。在KMP算法中,next数组是重要的组成部分,它存储了模式串的前缀中最长的相等的前缀和后缀的长度。 创建顺序串实现KMP模式匹配,可以通过以下步骤: 1.创建一个顺序串,可以使用数组或者类似于字符串的方式实现。 2.读入模式串和文本串,将它们分别存入两个顺序串中。 3.通过next数组,优化模式串的匹配。next数组的计算过程可以采用递归或迭代的方式实现。 4.在匹配时,从文本串的第一个字符开始,依次比较模式串的字符和文本串的字符,如果匹配则继续比较下一个字符。如果不匹配,就利用next数组跳过已经匹配成功的部分,继续向后匹配。 5.如果匹配成功,则返回匹配的位置;否则继续比较,直到文本串的所有字符都比较完毕。 优化next数组的计算,可以通过以下步骤: 1.初始化next数组,将第一个元素设为0,第二个元素设为1。 2.从第3个元素开始,依次比较前一个元素和模式串的前缀后缀。 3.如果相同,则将next数组中的元素赋值为前一个元素的值加1;否则继续比较下一个前缀和后缀。 4.如果没有相同的前缀和后缀,则将next数组中的元素赋值为0。 5.重复以上步骤,直到计算出next数组中所有的元素。 通过以上步骤,我们就可以创建顺序串并实现KMP模式匹配优化next数组。这个算法可以有效地提高匹配的效率,节约时间和空间。 ### 回答3: KMP模式匹配算法是一种高效的字符串匹配算法,它的核心在于构建next数组。而优化next数组的过程,可以通过创建顺序串的数据结构来实现。 首先,我们需要了解什么是顺序串。顺序串是一种基于数组的线性数据结构,可以用来存储字符序列。我们可以通过封装数组的方式,添加一些操作方法来实现顺序串的创建。具体来说,我们可以定义如下的结构体: ``` typedef struct SeqString { char* data; // 数据存储区 size_t length; // 串的长度 } SeqString; ``` 其中,`data`成员用于存储串的数据,`length`表示串的长度。接下来,我们可以定义创建顺序串的函数: ``` SeqString* createSeqString(char* str, size_t len) { SeqString* s = (SeqString*)malloc(sizeof(SeqString)); s->data = (char*)malloc((len + 1) * sizeof(char)); memcpy(s->data, str, len); s->data[len] = '\0'; s->length = len; return s; } ``` 在创建顺序串之后,我们可以对其进行遍历,构建next数组。KMP模式匹配算法中的next数组表示模式串中,以每个位置为结尾的子串中,前缀和后缀相等的最大长度。为了构建next数组,我们可以采用两个指针i和j,分别指向模式串的第一个字符和第二个字符,并通过遍历模式串的方式,依次计算得到next数组。 具体的算法流程如下: 1. 初始化i=0,j=-1,next[0]=-1 2. 判断j是否小于0或者s[i] == s[j],如果满足,则令i=i+1,j=j+1,next[i] = j 3. 如果s[i] ≠ s[j],则更新j=next[j] 4. 重复进行步骤2和3,直到i等于模式串的长度为止 通过这种方式构建得到的next数组,可以在模式匹配过程中,实现更高效的匹配。具体的匹配过程,可以采用next数组计算当前的模式串与主串的匹配偏移量,通过不重复的移动主串和模式串的方式,来实现快速的匹配。 总的来说,优化next数组的过程是非常重要的,可以大大提升KMP模式匹配算法的效率。通过创建顺序串的数据结构,我们可以方便地实现这一优化过程,得到更高效的字符串匹配算法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值